package cn.sylinx.hbatis.ext.proxy.command;

import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import cn.sylinx.hbatis.exception.HbatisException;
import cn.sylinx.hbatis.kit.ClassUtil;
import cn.sylinx.hbatis.log.GLog;

public enum CommandRegistry {

	INSTANCE;

	private final Map<Class<?>, CommandProxyFactory<?>> knownCommands = new HashMap<Class<?>, CommandProxyFactory<?>>();

	public <T> boolean hasCommand(Class<T> type) {
		return knownCommands.containsKey(type);
	}

	public <T> void addCommond(Class<T> type) {
		if (type.isInterface()) {
			if (hasCommand(type)) {
				throw new HbatisException("Type " + type + " is already known to the CommandRegistry.");
			}
			knownCommands.put(type, new CommandProxyFactory<T>(type));
			GLog.debug("hbatis command: {} registry ok", type.getName());
		}
	}

	@SuppressWarnings("unchecked")
	public <T> void addCommond(String className) {

		Class<T> type = null;
		ClassLoader clToUse = ClassUtil.getDefaultClassLoader();

		try {
			type = (Class<T>) Class.forName(className, false, clToUse);
		} catch (Exception e) {
			GLog.error("no class defined", e);
		}

		if (type == null) {
			return;
		}

		addCommond(type);
	}

	@SuppressWarnings("unchecked")
	public <T> T getCommand(Class<T> type) {
		final CommandProxyFactory<T> commandProxyFactory = (CommandProxyFactory<T>) knownCommands.get(type);
		if (commandProxyFactory == null) {
			throw new HbatisException("Type " + type + " is not known to the CommandRegistry.");
		}
		try {
			return commandProxyFactory.newInstance(new CommandProxy<T>(type));
		} catch (Exception e) {
			throw new HbatisException("Error getting command instance. Cause: " + e, e);
		}
	}

	public Collection<Class<?>> getCommands() {
		return Collections.unmodifiableCollection(knownCommands.keySet());
	}

}
